classdef C_Eval < handle
    %C_EVAL Summary of this class goes here
    %   Detailed explanation goes here
    
    properties (GetAccess='private', SetAccess='private')
        IterationCount = 0;
        BitErrorRate = 0;
        SymbolErrorRate = 0;
        
        TxObj;
        RxObj;
        ChObj;
    end

    methods (Access = private)
        function this = C_Eval(TxObj, RxObj, ChObj)
            this.TxObj = TxObj;
            this.RxObj = RxObj;
            this.ChObj = ChObj;
        end
    end    
    
    methods (Static)
        function this = Instance(TxObj, RxObj, ChObj)
             persistent this_obj;
             
             if isempty(this_obj) || ~isvalid(this_obj)
                this_obj = C_Eval(TxObj, RxObj, ChObj);
             end
             
             this = this_obj;            
        end    
        
        function Initialise(this)
            this.BitErrorRate = zeros(1, length(this.ChObj.Param.EbPerNo));
            this.SymbolErrorRate = zeros(1, length(this.ChObj.Param.EbPerNo));
        end
        
        function Eval(this)
%             plotSignal(this);             %do not execut this on multiple EbPerNo simulation
%             plotConstellation(this);      %do not execut this on multiple EbPerNo simulation
%             plotSpectrum(this);           %do not execut this on multiple EbPerNo simulation
%             GenerateConstellationMovie(this);    %do not execut this on multiple EbPerNo simulation
            
            this.IterationCount = this.IterationCount + 1;
            calcBER(this);
            calcSER(this);
        end
        
        function DisplayErrorRate(this)
            fprintf('BER: %g\n',this.BitErrorRate);            
            figure(30000);
            semilogy(this.ChObj.Param.EbPerNo, this.BitErrorRate, '-o');
            grid on;
            ylabel('BER'); xlabel('Eb/No (dB)');

            fprintf('SER: %g\n',this.SymbolErrorRate);           
            figure(40000);
            semilogy(this.ChObj.Param.EbPerNo, this.SymbolErrorRate, 'r-o');
            grid on;
            ylabel('SER'); xlabel('Eb/No (dB)'); 
            
%             fprintf('SER: %g\n',this.SymbolErrorRate);           
%             figure(40001);
%             semilogy(10*log10(this.ChObj.EsPerNo), this.SymbolErrorRate, 'r-o');
%             grid on;
%             ylabel('SER'); xlabel('Es/No (dB)');             
        end
        
        function SaveErrorRate(this)
            saveStr = sprintf('%s_1023SC_Rayleigh_results.mat', this.TxObj.Param.ActiveModType);
            
            BER = this.BitErrorRate;
            save(saveStr ,'BER');
%             SER = this.SymbolErrorRate;
%             save(saveStr , 'SER', '-append');
        end
    end
    
    
end

function plotSignal(this)
    figure(1);hold on;
    t = (0:length(this.TxObj.Signal)-1) / this.TxObj.Param.Fs;
    t2 = (0:length(this.RxObj.RawRxSignal)-1) / this.RxObj.Param.Fs;
    plot(t, this.TxObj.Signal, 'b');
    plot(t2, this.RxObj.RawRxSignal, 'r--');
    title('Tx & Rx time domain signal');
    xlabel('Time');
    ylabel('Magitude');
    legend('Tx', 'Rx');
end

function plotConstellation(this)
    for blockIndex = 1:this.TxObj.Param.numOfdmBlocks
        figure(10000 + blockIndex);hold on;
        
%         plot(this.TxObj.Symbols(:,blockIndex),'+');
        plot(this.RxObj.RawConstellation(this.TxObj.Param.ActiveModBand,blockIndex),'r.');
        plot(this.RxObj.EqualisedConstellation(this.TxObj.Param.ActiveModBand,blockIndex),'g^');

        plot(this.RxObj.RawConstellation(this.TxObj.Param.PilotModBand,blockIndex),'mv');

%         axis([min(min(real(this.TxObj.Symbols)))*3 ...
%               max(max(real(this.TxObj.Symbols)))*3 ...
%               min(min(imag(this.TxObj.Symbols)))*3 ...
%               max(max(imag(this.TxObj.Symbols)))*3]); 
          axis([-2 2 -2 2]);
        title('Constellation diagram compare in Z-plane')
        ylabel('Imaginary'); xlabel('Real');
        legend('Tx','Rx','Rx corrected');
    end
end

function GenerateConstellationMovie(this)
    ConstFrame = [];
    figureName = 30000;
    figure(figureName);hold on;
%     saveStr = sprintf('%s_w_doppler_%dHz.mat', this.TxObj.Param.ActiveModType, this.ChObj.Param.FdMax);
%     AviNameStr = sprintf('%s_w_doppler_%dHz.avi', this.TxObj.Param.ActiveModType, this.ChObj.Param.FdMax);
   
    for packetIndex = 1:this.TxObj.Param.numPackets
        for blockIndex = 1:this.TxObj.Param.numOfdmBlocks           
            clf(figureName);hold on;
            plot(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers),'+');
            plot(this.RxObj.RawSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers),'r.'); %warning: Rx doesn't have packetObj yet, might need to change in future
            axis([min(real(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers)))*3 ...
                  max(real(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers)))*3 ... 
                  min(imag(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers)))*3 ...
                  max(imag(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}(2:this.TxObj.Param.numSubcarriers)))*3]); 
            title('Constellation diagram compare in Z-plane')
            ylabel('Imaginary'); xlabel('Real');
            legend('Tx','Rx','Rx corrected');
            
            tempFrame = getframe(gcf);
%             ConstFrame = [ConstFrame tempFrame]; 
        end
    end
    
%     save(saveStr, 'ConstFrame');
%     movie2avi(ConstFrame, AviNameStr, 'compression', 'FFDS');
end

function plotSpectrum(this)
    F = (1:this.TxObj.Param.numSubcarriers*2) * this.TxObj.Param.Fs / (this.TxObj.Param.numSubcarriers*2);
    for packetIndex = 1:this.TxObj.Param.numPackets
        for blockIndex = 1:this.TxObj.Param.numOfdmBlocks
            figure(20000 + packetIndex*100 + blockIndex);hold on;
            plot(F, abs(this.TxObj.packetObj{packetIndex}.ActiveSubsSpectrum{blockIndex}),'+');
            plot(F, abs(this.RxObj.RawSpectrum{blockIndex}),'r.'); %warning: Rx doesn't have packetObj yet, might need to change in future           
            title('Spectrum')
            ylabel('Magnitude'); xlabel('Frequency');
            legend('Tx','Rx');
        end
    end
end

function calcBER(this)
    locBER = sum(xor(repmat(this.TxObj.TxDataBin, [length(this.ChObj.Param.EbPerNo) 1]), this.RxObj.RxDataBin), 2) / length(this.TxObj.TxDataBin);
    this.BitErrorRate = (this.BitErrorRate * (this.IterationCount - 1) + locBER') / this.IterationCount;  
end

function calcSER(this)
    locSER = zeros(1,length(this.ChObj.Param.EbPerNo));
    NumErrors = zeros(1,length(this.ChObj.Param.EbPerNo));
    
    for snrIndex = 1:length(this.ChObj.Param.EbPerNo)
        [NumErrors(snrIndex), locSER(snrIndex)] = symerr(this.TxObj.TxDataDec', this.RxObj.RxDataDec(snrIndex,:));     
    end
    
    this.SymbolErrorRate = (this.SymbolErrorRate * (this.IterationCount - 1) + locSER) / this.IterationCount;       
end
